﻿using System;
using System.Collections.Generic;
using System.Linq;
using ETP;
using NymphsLib.CommonUtilities;
using StringExtension = NymphsLib.CommonUtilities.Extensions.StringExtension;

namespace MovieBrowser.Parser
{
    public static class ImdbParser
    {
        public const string KEY_NumVoters = "RatingCount";
        public const string KEY_NumCritic = "CriticReview";
        public const string KEY_MetaScore = "MetaCritic";

        public const string KEY_Runtime = "Runtime";
        public const string KEY_Rating = "Rating";
        public const string KEY_Title = "Title";
        public const string KEY_Year = "Year";
        public const string KEY_ReleaseDate = "ReleaseDate";
        public const string KEY_Mpaa = "Mpaa";
        public const string KEY_MpaaReason = "MpaaReason";
        public const string KEY_Genres = "Genres";
        public const string KEY_ImdbId = "ImdbId";
        public const string KEY_Countries = "Countries";
        public const string KEY_Highlight = "Highlight";
        public const string KEY_Storyline = "Storyline";
        public const string KEY_Languages = "Languages";
        private const string KEY_Persons = "Persons";
        public const string KEY_Directors = "Directors";
        public const string KEY_Writers = "Writers";
        public const string KEY_Stars = "Stars";
        public const string KEY_Actors = "Actors";
        public const string KEY_Characters = "Characters";
        public const string KEY_MediaFrom = "MediaFrom";
        public const string KEY_Keywords = "Keywords";
        public const string KEY_Poster = "Poster";
        public const string KEY_Guess = "Guess";
        public const string KEY_PersonId = "PersonId";
        public const string KEY_PersonName = "PersonName";
        public const string KEY_PersonBirthday = "PersonBirthday";
        public const string KEY_PersonDeathday = "PersonDeathday";
        public const string KEY_PersonHeight = "PersonHeight";
        public const string KEY_PersonProfession = "PersonProfession";
        public const string KEY_PersonBirthplace = "PersonBirthplace";
        public const string KEY_PersonCountry = "PersonCountry";

        private static EasyTextParser _easyTextParser = new EasyTextParser();


        //public static readonly Dictionary<string, ParseRule> Rules = new Dictionary<string, ParseRule>();


        public static void LoadRules(string xmlPath)
        {
            _easyTextParser.LoadRules(xmlPath);

            //var serializer = new XmlSerializer(typeof (ParsingRules));
            //var reader = new StreamReader(xmlPath);
            //var rules = (ParsingRules) serializer.Deserialize(reader);
            //foreach (ParseRule parseRule in rules.Rules)
            //{
            //    Rules[parseRule.Name] = parseRule;
            //}
            //reader.Close();
        }

        public static Dictionary<string, object> ParseMovie(string html)
        {
            try
            {
                Dictionary<string, object> movie = new Dictionary<string, object>();

                movie[KEY_Rating] = Convert.ToDouble(ParseStringContent(html, KEY_Rating));
                movie[KEY_Title] = HttpHelper.HtmlDecode(HttpHelper.UrlDecode(ParseStringContent(html, KEY_Title)));
                movie[KEY_Year] = Convert.ToInt32(ParseStringContent(html, KEY_Year));
                movie[KEY_ImdbId] = ParseStringContent(html, KEY_ImdbId);
                //movie[KEY_FilePath] = "";
                movie[KEY_Runtime] = ParseStringContent(html, KEY_Runtime);
                movie[KEY_Mpaa] = ParseStringContent(html, KEY_Mpaa);
                movie[KEY_MpaaReason] = ParseStringContent(html, KEY_MpaaReason);
                movie[KEY_Highlight] = ParseStringContent(html, KEY_Highlight);
                movie[KEY_Genres] = ParseGenres(html);
                movie[KEY_Countries] = ParseCountries(html);
                movie[KEY_Languages] = ParseLanguages(html);
                movie[KEY_ReleaseDate] = ParseStringContent(html, KEY_ReleaseDate);
                movie[KEY_Storyline] = ParseStringContent(html, KEY_Storyline);
                movie[KEY_NumVoters] = ParseStringContent(html, KEY_NumVoters);
                movie[KEY_NumCritic] = ParseStringContent(html, KEY_NumCritic);
                movie[KEY_MetaScore] = ParseStringContent(html, KEY_MetaScore);

                try { movie[KEY_Stars] = ParseStars(html); }
                catch (Exception exception)
                {
                    movie[KEY_Stars] = new List<Dictionary<string, object>>();
                    Logger.Exception(exception, 83, "Person Stars");
                }
                try { movie[KEY_Writers] = ParseWriters(html); }
                catch (Exception exception)
                {
                    movie[KEY_Writers] = new List<Dictionary<string, object>>();
                    Logger.Exception(exception, 85, "Person Writers");
                }
                try { movie[KEY_Directors] = ParseDirectors(html); }
                catch (Exception exception)
                {
                    movie[KEY_Directors] = new List<Dictionary<string, object>>();
                    Logger.Exception(exception, 87, "Person Directors");
                }
                try { movie[KEY_Characters] = ParseCharacters(html); }
                catch (Exception exception)
                {
                    movie[KEY_Characters] = new List<Dictionary<string, object>>();
                    Logger.Exception(exception, 89, "Person Actors");
                }
                return movie;
            }
            catch (Exception exception)
            {
                Logger.Exception(exception, 418);
                return null;
            }
        }

        /*
        private static object Parse(string html, ParseRule rule)
        {
            var list = new List<string>();
            if (rule.ParentRule != null)
            {
                var obj = Parse(html, rule.ParentRule);
                if (obj is string)
                    list.Add((string)obj);
                else if (obj is string[])
                    list.AddRange((string[])obj);
                else if (obj is List<string>)
                    list.AddRange((List<string>)obj);
            }
            else
            {
                list.Add(html);
            }

            try
            {
                var list1 = new List<string>();
                var list2 = new List<string[]>();

                foreach (string subject in list)
                {
                    var matches = Regex.Matches(subject, rule.Pattern, rule.Options);
                    foreach (Match match in matches)
                    {
                        var result = StringExtension.Clean(match.Result(rule.Replacement)).Trim();
                        if (!string.IsNullOrEmpty(rule.ReplaceText))
                            result = StringExtension.Clean(Regex.Replace(result, rule.ReplaceText, rule.ReplaceWithText + "", RegexOptions.Singleline)).Trim();

                        if (string.IsNullOrEmpty(rule.Separator))
                            list1.Add(result);
                        else
                            list2.Add(result.Split(string.IsNullOrEmpty(rule.Separator) ? ",".ToCharArray() : rule.Separator.ToCharArray()));
                    }
                }

                if (rule.IsArray)
                    if (string.IsNullOrEmpty(rule.Separator))
                        return list1;               //returns list of strings
                    else
                        return list2;               //return list of string arrays
                else
                    if (string.IsNullOrEmpty(rule.Separator))
                    {
                        if (rule.IsJoined)
                            return string.Join(" ", list1.ToArray());
                        else
                            return list1.FirstOrDefault();  //Returns strings
                    }
                    else
                        return list2.FirstOrDefault();  //Returns array of strings
            }
            catch (Exception exception)
            {
                Logger.Exception(exception, 123, "Mismatch: " + rule.Name);
                if (rule.IsArray)
                    return new string[0];
                else
                    return "";
            }
        }*/

        //        public static IEnumerable<T> ParseAsList<T>(string html, ParseRule rule)
        //        {
        //            var list = new List<string>();
        //            if (rule.ParentRule != null)
        //            {
        //                var obj = Parse(html, rule.ParentRule);
        //                if (obj is string)
        //                    list.Add((string)obj);
        //                else if (obj is string[])
        //                    list.AddRange((string[])obj);
        //                else if (obj is List<string>)
        //                    list.AddRange((List<string>)obj);
        //                else if (obj is List<object>)
        //                {
        //                    list.AddRange(((List<object>)obj).Cast<string>());
        //                }
        //
        //            }
        //            else
        //            {
        //                list.Add(html);
        //            }
        //
        //            var objects = new List<T>();
        //
        //            foreach (var subject in list)
        //            {
        //                var matchCollection = Regex.Matches(subject, rule.Pattern, rule.Options);
        //                foreach (Match match in matchCollection)
        //                {
        //                    var result = match.Result(rule.Replacement).Clean().Trim();
        //                    if (!string.IsNullOrEmpty(rule.ReplaceText))
        //                        result = Regex.Replace(result, rule.ReplaceText, rule.ReplaceWithText + "", RegexOptions.Singleline);
        //
        //                    if (rule.IsArray)
        //                    {
        //                        object strarr = result.Split(string.IsNullOrEmpty(rule.Separator)
        //                                                      ? ",".ToCharArray()
        //                                                      : rule.Separator.ToCharArray());
        //                        objects.Add((T)strarr);
        //                    }
        //                    else
        //                    {
        //                        objects.Add((T)(object)result);
        //                    }
        //                }
        //            }
        //            return objects;
        //        }

        //        public static string ParseRating(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Rating, html);
        //        }
        //
        //        public static string ParseTitle(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Title, html);
        //        }
        //
        //        public static string ParseYear(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Year, html);
        //        }

        //        public static string ParseRuntime(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Runtime, html);//(html, Rules[KEY_Runtime]);
        //        }
        //
        //        public static string ParseReleaseDate(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_ReleaseDate, html);//(html, Rules[KEY_ReleaseDate]);
        //        }
        //
        //        public static string ParseMpaa(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Mpaa, html);//(html, Rules[KEY_Mpaa]);
        //        }

        //        public static string ParseMpaaReason(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_MpaaReason, html);//(html, Rules[KEY_MpaaReason]);
        //        }

        public static List<Dictionary<string, object>> ParseGenres(string html)
        {
            var list = (List<string[]>)_easyTextParser.Parse(KEY_Genres, html);//Parse(html, Rules[KEY_Genres]);
            var list2 = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["Code"] = m[0];
                item["Name"] = m[1];
                list2.Add(item);
            }
            return list2;
            //return (from string[] m in list select new Genre { Code = m[0], Name = m[1] }).ToList();
        }

        public static List<Dictionary<string, object>> ParseCountries(string html)
        {
            var list = (List<string[]>)_easyTextParser.Parse(KEY_Countries, html);//Parse(html, Rules[KEY_Countries]);
            var list2 = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["Code"] = m[0];
                item["Name"] = m[1];
                list2.Add(item);
            }
            return list2;
            //return (from string[] m in list select new Country { Code = m[0], Name = m[1] }).ToList();
        }

        //        public static string ParseId(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_ImdbId, html);//Parse(html, Rules[KEY_ImdbId]);
        //        }

        public static List<Dictionary<string, object>> ParseKeywords(string html)
        {
            var list = (List<string[]>)_easyTextParser.Parse(KEY_Keywords, html);//Parse(html, Rules[KEY_Keywords]);
            var list2 = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["Code"] = m[0];
                item["Name"] = m[1];
                list2.Add(item);
            }
            return list2;
            //return (from string[] m in list select new Keyword { Code = m[0], Name = m[1] }).ToList();
        }
        //
        //        public static string ParseMoviePoster(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_Poster, html);//Parse(html, Rules[KEY_Poster]);
        //        }
        //
        //
        //        public static string ImdbMediaFrom(string html)
        //        {
        //            return (string)_easyTextParser.Parse(KEY_MediaFrom, html);//Parse(html, Rules[KEY_MediaFrom]);
        //        }

        public static Dictionary<string, object> GuessMovie(string html)
        {
            var movie = new Dictionary<string, object>();

            //First check if its a valid movie page. 
            var title = ParseStringContent(html, KEY_Title);
            var imdbId = ParseStringContent(html, KEY_ImdbId);
            int year;
            if (!string.IsNullOrEmpty(title) && !string.IsNullOrEmpty(imdbId))
            {
                //This is the movie page
                movie[KEY_ImdbId] = imdbId;
                movie[KEY_Title] = title;
                Int32.TryParse(ParseStringContent(html, KEY_Year), out year);
                movie[KEY_Year] = year;
            }
            else
            {
                //Ok Guess It.
                var match = (string[])_easyTextParser.Parse(KEY_Guess, html);//Parse(html, Rules[KEY_Guess]);
                if (match.Length > 0)
                {
                    movie[KEY_ImdbId] = match[0];
                    movie[KEY_Title] = match[1];
                    Int32.TryParse(match[2], out year);
                    movie[KEY_Year] = year;
                }
                else
                {
                    return null;
                }
            }
            return movie;
        }



        public static string ParseStringContent(string html, string key)
        {
            var content = (string)_easyTextParser.Parse(key, html);
            if (content != null)
                content = content.Trim();
            return content;
        }

        public static List<Dictionary<string, object>> ParseLanguages(string html)
        {
            var list = (List<string[]>)_easyTextParser.Parse(KEY_Languages, html);
            var list2 = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["Code"] = m[0];
                item["Name"] = m[1];
                list2.Add(item);
            }
            return list2;
        }

        private static List<Dictionary<string, object>> ToPersonEntry(IEnumerable<string[]> list)
        {
            var listPersonEntry = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["ImdbId"] = m[0];
                item["Name"] = m[1];
                listPersonEntry.Add(item);
            }
            return listPersonEntry;
        }

        public static List<Dictionary<string, object>> ParseDirectors(string html)
        {
            var str = (List<string[]>)_easyTextParser.Parse(KEY_Directors, html);//Parse(html, Rules[KEY_Directors]);
            return ToPersonEntry(str);
        }

        public static List<Dictionary<string, object>> ParseWriters(string html)
        {
            var str = (List<string[]>)_easyTextParser.Parse(KEY_Writers, html);//Parse(html, Rules[KEY_Writers]);
            return ToPersonEntry(str);
        }

        public static List<Dictionary<string, object>> ParseStars(string html)
        {
            var str = (List<string[]>)_easyTextParser.Parse(KEY_Stars, html);//Parse(html, Rules[KEY_Stars]);
            return ToPersonEntry(str);
        }

        public static List<Dictionary<string, object>> ParseCharacters(string html)
        {
            //var table = (string)_easyTextParser.Parse(KEY_Actors, html);//Parse(html, Rules[KEY_Actors]);

            var list = (List<string[]>)_easyTextParser.Parse(KEY_Characters, html);//Parse(table, Rules[KEY_Characters]);
            var list2 = new List<Dictionary<string, object>>();
            foreach (string[] m in list)
            {
                var item = new Dictionary<string, object>();
                item["Person"] = new Dictionary<string, object>();
                var person = (Dictionary<string, object>)item["Person"];
                person["ImdbId"] = m[0];
                person["Name"] = m[1];
                item["CharacterName"] = m[3];
                item["CharacterId"] = m[2];
                list2.Add(item);
            }
            return list2;
            //            return (from string[] m in list
            //select new Actor
            //    {
            //        Person = new Person { ImdbId = m[0], Name = m[1] },
            //        CharacterName = m[3],
            //        CharacterId = m[2]
            //    }).ToList();
        }

        public static Dictionary<string, object> ParsePerson(string html)
        {
            try
            {
                var person = new Dictionary<string, object>();

                person[KEY_PersonName] = StringExtension.StripTags(ParsePersonName(html));
                person[KEY_PersonId] = StringExtension.StripTags(ParsePersonId(html));
                person[KEY_PersonBirthday] = ParsePersonBirthday(html);
                person[KEY_PersonDeathday] = ParsePersonDeathday(html);
                person[KEY_PersonHeight] = StringExtension.StripTags(ParsePersonHeight(html));
                person[KEY_Highlight] = StringExtension.StripTags(ParsePersonHighlight(html));
                person[KEY_PersonProfession] = StringExtension.StripTags(ParsePersonProfession(html));
                person[KEY_PersonBirthplace] = StringExtension.StripTags(ParseBirthplace(html));

                if (person[KEY_PersonBirthplace] != null)
                {
                    person[KEY_PersonCountry] = ((string)person[KEY_PersonBirthplace]).Split(",".ToCharArray()).LastOrDefault().Trim();
                }

                return person;
            }
            catch (Exception exception)
            {
                Logger.Exception(exception, 418);
                return null;
            }
        }

        private static string ParseBirthplace(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonBirthplace, html);//Parse(html, Rules[KEY_PersonBirthplace]);
            return result;
        }

        private static string ParsePersonProfession(string html)
        {
            var result = (List<string>)_easyTextParser.Parse(KEY_PersonProfession, html);//Parse(html, Rules[KEY_PersonProfession]);
            string join = string.Join(", ", result.ToArray());
            return join;
        }

        private static string ParsePersonHeight(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonHeight, html);//Parse(html, Rules[KEY_PersonHeight]);
            return result;
        }

        private static DateTime? ParsePersonDeathday(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonDeathday, html);//Parse(html, Rules[KEY_PersonDeathday]);
            try
            {
                var dateTime = DateTime.Parse(result);
                return dateTime;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static DateTime? ParsePersonBirthday(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonBirthday, html);//Parse(html, Rules[KEY_PersonBirthday]);
            try
            {
                DateTime dateTime = DateTime.Parse(result);
                return dateTime;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static string ParsePersonHighlight(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_Highlight, html);//Parse(html, Rules[KEY_Highlight]);
            return result;
        }

        private static string ParsePersonId(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonId, html);//Parse(html, Rules[KEY_PersonId]);
            return result;
        }

        private static string ParsePersonName(string html)
        {
            var result = (string)_easyTextParser.Parse(KEY_PersonName, html);//Parse(html, Rules[KEY_PersonName]);
            return result;
        }
    }
}